///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
 *	Contains AABB-related code.
 *	\file		IceAABB.cpp
 *	\author		Pierre Terdiman
 *	\date		January, 29, 2000
 */
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
 *	AABB class.
 *	\class		AABB
 *	\author		Pierre Terdiman
 *	\version	1.0
 */
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Precompiled Header
#include "Stdafx.h"

using namespace IceMaths;

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
 *	Computes the sum of two AABBs.
 *	\param		aabb	[in] the other AABB
 *	\return		Self-Reference
 */
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
AABB& AABB::Add(const AABB& aabb)
{
	// Compute new min & max values
	Point Min;	GetMin(Min);
	Point Tmp;	aabb.GetMin(Tmp);
	Min.Min(Tmp);

	Point Max;	GetMax(Max);
	aabb.GetMax(Tmp);
	Max.Max(Tmp);

	// Update this
	SetMinMax(Min, Max);
	return *this;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
 *	Makes a cube from the AABB.
 *	\param		cube	[out] the cube AABB
 *	\return		cube edge length
 */
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
float AABB::MakeCube(AABB& cube) const
{
	Point Ext;	GetExtents(Ext);
	float Max = Ext.Max();

	Point Cnt;	GetCenter(Cnt);
	cube.SetCenterExtents(Cnt, Point(Max, Max, Max));
	return Max;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
 *	Makes a sphere from the AABB.
 *	\param		sphere	[out] sphere containing the AABB
 */
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void AABB::MakeSphere(Sphere& sphere) const
{
	GetExtents(sphere.mCenter);
	sphere.mRadius = sphere.mCenter.Magnitude() * 1.00001f;	// To make sure sphere::Contains(*this)	succeeds
	GetCenter(sphere.mCenter);
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
 *	Checks a box is inside another box.
 *	\param		box		[in] the other AABB
 *	\return		true if current box is inside input box
 */
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool AABB::IsInside(const AABB& box) const
{
	if(box.GetMin(0)>GetMin(0))	return false;
	if(box.GetMin(1)>GetMin(1))	return false;
	if(box.GetMin(2)>GetMin(2))	return false;
	if(box.GetMax(0)<GetMax(0))	return false;
	if(box.GetMax(1)<GetMax(1))	return false;
	if(box.GetMax(2)<GetMax(2))	return false;
	return true;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
 *	Computes the AABB planes.
 *	\param		planes	[out] 6 planes surrounding the box
 *	\return		true if success
 */
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool AABB::ComputePlanes(Plane* planes)	const
{
	// Checkings
	if(!planes)	return false;

	Point Center, Extents;
	GetCenter(Center);
	GetExtents(Extents);

	// Writes normals
	planes[0].n = Point(1.0f, 0.0f, 0.0f);
	planes[1].n = Point(-1.0f, 0.0f, 0.0f);
	planes[2].n = Point(0.0f, 1.0f, 0.0f);
	planes[3].n = Point(0.0f, -1.0f, 0.0f);
	planes[4].n = Point(0.0f, 0.0f, 1.0f);
	planes[5].n = Point(0.0f, 0.0f, -1.0f);

	// Compute a point on each plane
	Point p0 = Point(Center.x+Extents.x, Center.y, Center.z);
	Point p1 = Point(Center.x-Extents.x, Center.y, Center.z);
	Point p2 = Point(Center.x, Center.y+Extents.y, Center.z);
	Point p3 = Point(Center.x, Center.y-Extents.y, Center.z);
	Point p4 = Point(Center.x, Center.y, Center.z+Extents.z);
	Point p5 = Point(Center.x, Center.y, Center.z-Extents.z);

	// Compute d
	planes[0].d = -(planes[0].n|p0);
	planes[1].d = -(planes[1].n|p1);
	planes[2].d = -(planes[2].n|p2);
	planes[3].d = -(planes[3].n|p3);
	planes[4].d = -(planes[4].n|p4);
	planes[5].d = -(planes[5].n|p5);

	return true;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
 *	Computes the aabb points.
 *	\param		pts	[out] 8 box points
 *	\return		true if success
 */
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool AABB::ComputePoints(Point* pts)	const
{
	// Checkings
	if(!pts)	return false;

	// Get box corners
	Point min;	GetMin(min);
	Point max;	GetMax(max);

	//     7+------+6			0 = ---
	//     /|     /|			1 = +--
	//    / |    / |			2 = ++-
	//   / 4+---/--+5			3 = -+-
	// 3+------+2 /    y   z	4 = --+
	//  | /    | /     |  /		5 = +-+
	//  |/     |/      |/		6 = +++
	// 0+------+1      *---x	7 = -++

	// Generate 8 corners of the bbox
	pts[0] = Point(min.x, min.y, min.z);
	pts[1] = Point(max.x, min.y, min.z);
	pts[2] = Point(max.x, max.y, min.z);
	pts[3] = Point(min.x, max.y, min.z);
	pts[4] = Point(min.x, min.y, max.z);
	pts[5] = Point(max.x, min.y, max.z);
	pts[6] = Point(max.x, max.y, max.z);
	pts[7] = Point(min.x, max.y, max.z);

	return true;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
 *	Gets vertex normals.
 *	\param		pts	[out] 8 box points
 *	\return		true if success
 */
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
const Point* AABB::GetVertexNormals()	const
{
	static float VertexNormals[] = 
	{
		-INVSQRT3,	-INVSQRT3,	-INVSQRT3,
		INVSQRT3,	-INVSQRT3,	-INVSQRT3,
		INVSQRT3,	INVSQRT3,	-INVSQRT3,
		-INVSQRT3,	INVSQRT3,	-INVSQRT3,
		-INVSQRT3,	-INVSQRT3,	INVSQRT3,
		INVSQRT3,	-INVSQRT3,	INVSQRT3,
		INVSQRT3,	INVSQRT3,	INVSQRT3,
		-INVSQRT3,	INVSQRT3,	INVSQRT3
	};
	return (const Point*)VertexNormals;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
 *	Returns edges.
 *	\return		24 indices (12 edges) indexing the list returned by ComputePoints()
 */
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
const udword* AABB::GetEdges() const
{
	static udword Indices[] = {
	0, 1,	1, 2,	2, 3,	3, 0,
	7, 6,	6, 5,	5, 4,	4, 7,
	1, 5,	6, 2,
	3, 7,	4, 0
	};
	return Indices;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
 *	Returns edge normals.
 *	\return		edge normals in local space
 */
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
const Point* AABB::GetEdgeNormals() const
{
	static float EdgeNormals[] = 
	{
		0,			-INVSQRT2,	-INVSQRT2,	// 0-1
		INVSQRT2,	0,			-INVSQRT2,	// 1-2
		0,			INVSQRT2,	-INVSQRT2,	// 2-3
		-INVSQRT2,	0,			-INVSQRT2,	// 3-0

		0,			INVSQRT2,	INVSQRT2,	// 7-6
		INVSQRT2,	0,			INVSQRT2,	// 6-5
		0,			-INVSQRT2,	INVSQRT2,	// 5-4
		-INVSQRT2,	0,			INVSQRT2,	// 4-7

		INVSQRT2,	-INVSQRT2,	0,			// 1-5
		INVSQRT2,	INVSQRT2,	0,			// 6-2
		-INVSQRT2,	INVSQRT2,	0,			// 3-7
		-INVSQRT2,	-INVSQRT2,	0			// 4-0
	};
	return (const Point*)EdgeNormals;
}

// ===========================================================================
//  (C) 1996-98 Vienna University of Technology
// ===========================================================================
//  NAME:       bboxarea
//  TYPE:       c++ code
//  PROJECT:    Bounding Box Area
//  CONTENT:    Computes area of 2D projection of 3D oriented bounding box
//  VERSION:    1.0
// ===========================================================================
//  AUTHORS:    ds      Dieter Schmalstieg
//              ep      Erik Pojar
// ===========================================================================
//  HISTORY:
//
//  19-sep-99 15:23:03  ds      last modification
//  01-dec-98 15:23:03  ep      created
// ===========================================================================

//----------------------------------------------------------------------------
// SAMPLE CODE STARTS HERE
//----------------------------------------------------------------------------

// NOTE: This sample program requires OPEN INVENTOR!

//indexlist: this table stores the 64 possible cases of classification of
//the eyepoint with respect to the 6 defining planes of the bbox (2^6=64)
//only 26 (3^3-1, where 1 is "inside" cube) of these cases are valid.
//the first 6 numbers in each row are the indices of the bbox vertices that
//form the outline of which we want to compute the area (counterclockwise
//ordering), the 7th entry means the number of vertices in the outline.
//there are 6 cases with a single face and and a 4-vertex outline, and
//20 cases with 2 or 3 faces and a 6-vertex outline. a value of 0 indicates
//an invalid case.


// Original list was made of 7 items, I added an 8th element:
// - to padd on a cache line
// - to repeat the first entry to avoid modulos
//
// I also replaced original ints with sbytes.

static const sbyte gIndexList[64][8] =
{
    {-1,-1,-1,-1,-1,-1,-1,   0}, // 0 inside
    { 0, 4, 7, 3, 0,-1,-1,   4}, // 1 left
    { 1, 2, 6, 5, 1,-1,-1,   4}, // 2 right
    {-1,-1,-1,-1,-1,-1,-1,   0}, // 3 -
    { 0, 1, 5, 4, 0,-1,-1,   4}, // 4 bottom
    { 0, 1, 5, 4, 7, 3, 0,   6}, // 5 bottom, left
    { 0, 1, 2, 6, 5, 4, 0,   6}, // 6 bottom, right
    {-1,-1,-1,-1,-1,-1,-1,   0}, // 7 -
    { 2, 3, 7, 6, 2,-1,-1,   4}, // 8 top
    { 0, 4, 7, 6, 2, 3, 0,   6}, // 9 top, left
    { 1, 2, 3, 7, 6, 5, 1,   6}, //10 top, right
    {-1,-1,-1,-1,-1,-1,-1,   0}, //11 -
    {-1,-1,-1,-1,-1,-1,-1,   0}, //12 -
    {-1,-1,-1,-1,-1,-1,-1,   0}, //13 -
    {-1,-1,-1,-1,-1,-1,-1,   0}, //14 -
    {-1,-1,-1,-1,-1,-1,-1,   0}, //15 -
    { 0, 3, 2, 1, 0,-1,-1,   4}, //16 front
    { 0, 4, 7, 3, 2, 1, 0,   6}, //17 front, left
    { 0, 3, 2, 6, 5, 1, 0,   6}, //18 front, right
    {-1,-1,-1,-1,-1,-1,-1,   0}, //19 -
    { 0, 3, 2, 1, 5, 4, 0,   6}, //20 front, bottom
    { 1, 5, 4, 7, 3, 2, 1,   6}, //21 front, bottom, left
    { 0, 3, 2, 6, 5, 4, 0,   6}, //22 front, bottom, right
    {-1,-1,-1,-1,-1,-1,-1,   0}, //23 -
    { 0, 3, 7, 6, 2, 1, 0,   6}, //24 front, top
    { 0, 4, 7, 6, 2, 1, 0,   6}, //25 front, top, left
    { 0, 3, 7, 6, 5, 1, 0,   6}, //26 front, top, right
    {-1,-1,-1,-1,-1,-1,-1,   0}, //27 -
    {-1,-1,-1,-1,-1,-1,-1,   0}, //28 -
    {-1,-1,-1,-1,-1,-1,-1,   0}, //29 -
    {-1,-1,-1,-1,-1,-1,-1,   0}, //30 -
    {-1,-1,-1,-1,-1,-1,-1,   0}, //31 -
    { 4, 5, 6, 7, 4,-1,-1,   4}, //32 back
    { 0, 4, 5, 6, 7, 3, 0,   6}, //33 back, left
    { 1, 2, 6, 7, 4, 5, 1,   6}, //34 back, right
    {-1,-1,-1,-1,-1,-1,-1,   0}, //35 -
    { 0, 1, 5, 6, 7, 4, 0,   6}, //36 back, bottom
    { 0, 1, 5, 6, 7, 3, 0,   6}, //37 back, bottom, left
    { 0, 1, 2, 6, 7, 4, 0,   6}, //38 back, bottom, right
    {-1,-1,-1,-1,-1,-1,-1,   0}, //39 -
    { 2, 3, 7, 4, 5, 6, 2,   6}, //40 back, top
    { 0, 4, 5, 6, 2, 3, 0,   6}, //41 back, top, left
    { 1, 2, 3, 7, 4, 5, 1,   6}, //42 back, top, right
    {-1,-1,-1,-1,-1,-1,-1,   0}, //43 invalid
    {-1,-1,-1,-1,-1,-1,-1,   0}, //44 invalid
    {-1,-1,-1,-1,-1,-1,-1,   0}, //45 invalid
    {-1,-1,-1,-1,-1,-1,-1,   0}, //46 invalid
    {-1,-1,-1,-1,-1,-1,-1,   0}, //47 invalid
    {-1,-1,-1,-1,-1,-1,-1,   0}, //48 invalid
    {-1,-1,-1,-1,-1,-1,-1,   0}, //49 invalid
    {-1,-1,-1,-1,-1,-1,-1,   0}, //50 invalid
    {-1,-1,-1,-1,-1,-1,-1,   0}, //51 invalid
    {-1,-1,-1,-1,-1,-1,-1,   0}, //52 invalid
    {-1,-1,-1,-1,-1,-1,-1,   0}, //53 invalid
    {-1,-1,-1,-1,-1,-1,-1,   0}, //54 invalid
    {-1,-1,-1,-1,-1,-1,-1,   0}, //55 invalid
    {-1,-1,-1,-1,-1,-1,-1,   0}, //56 invalid
    {-1,-1,-1,-1,-1,-1,-1,   0}, //57 invalid
    {-1,-1,-1,-1,-1,-1,-1,   0}, //58 invalid
    {-1,-1,-1,-1,-1,-1,-1,   0}, //59 invalid
    {-1,-1,-1,-1,-1,-1,-1,   0}, //60 invalid
    {-1,-1,-1,-1,-1,-1,-1,   0}, //61 invalid
    {-1,-1,-1,-1,-1,-1,-1,   0}, //62 invalid
    {-1,-1,-1,-1,-1,-1,-1,   0}  //63 invalid
};

const sbyte* AABB::ComputeOutline(const Point& local_eye, sdword& num)	const
{
	// Get box corners
	Point min;	GetMin(min);
	Point max;	GetMax(max);

	// Compute 6-bit code to classify eye with respect to the 6 defining planes of the bbox
	int pos = ((local_eye.x < min.x) ?  1 : 0)	// 1 = left
			+ ((local_eye.x > max.x) ?  2 : 0)	// 2 = right
			+ ((local_eye.y < min.y) ?  4 : 0)	// 4 = bottom
			+ ((local_eye.y > max.y) ?  8 : 0)	// 8 = top
			+ ((local_eye.z < min.z) ? 16 : 0)	// 16 = front
			+ ((local_eye.z > max.z) ? 32 : 0);	// 32 = back

	// Look up number of vertices in outline
	num = (sdword)gIndexList[pos][7];
	// Zero indicates invalid case
	if(!num) return null;

	return &gIndexList[pos][0];
}

// calculateBoxArea: computes the screen-projected 2D area of an oriented 3D bounding box

//const Point&		eye,		//eye point (in bbox object coordinates)
//const AABB&			box,		//3d bbox
//const Matrix4x4&	mat,		//free transformation for bbox
//float width, float height, int& num)
float AABB::ComputeBoxArea(const Point& eye, const Matrix4x4& mat, float width, float height, sdword& num)	const
{
	const sbyte* Outline = ComputeOutline(eye, num);
	if(!Outline)	return -1.0f;

	// Compute box vertices
	Point vertexBox[8], dst[8];
	ComputePoints(vertexBox);

	// Transform all outline corners into 2D screen space
	for(sdword i=0;i<num;i++)
	{
		HPoint Projected;
		vertexBox[Outline[i]].ProjectToScreen(width, height, mat, Projected);
		dst[i] = Projected;
	}

	float Sum = (dst[num-1][0] - dst[0][0]) * (dst[num-1][1] + dst[0][1]);

	for(int i=0; i<num-1; i++)
		Sum += (dst[i][0] - dst[i+1][0]) * (dst[i][1] + dst[i+1][1]);

	return Sum * 0.5f;	//return computed value corrected by 0.5
}
